
<!DOCTYPE html>
<html>
<head>
    <style>
        /* 基础重置 */
        body {
            margin: 0;
            padding: 20px;
            font-family: Arial, sans-serif;
        }

        /* 主容器 */
        .main-container {
            max-width: 1200px;
            margin: 0 auto;
        }

        /* 核心内容区 */
        .content-wrapper {
            display: flex;
            gap: 40px;
            flex-wrap: wrap;
            align-items: flex-start;
        }

        /* 田字棋区 */
        .tianziqi-section {
            flex: 0 0 auto;
            width: 200px; /* 固定田字棋区宽度 */
        }

        /* 组合区 */
        .combinations-section {
            flex: 1;
            min-width: 300px;
        }

        /* 输入区 */
        .input-group {
            display: flex;
            gap: 8px;
            margin-bottom: 20px;
            max-width: 360px;
        }

        input {
            width: 90px;
            padding: 8px;
            border: 2px solid #4CAF50;
            border-radius: 4px;
            font-size: 16px;
        }

        button {
            padding: 8px 16px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            white-space: nowrap;
        }

        /* 经典田字棋样式 */
        .tianziqi {
            border-collapse: collapse;
            margin: 12px 0;
            background: white;
        }

        .tianziqi td {
            width: 50px;
            height: 50px;
            border: 2px solid #ccc;
            text-align: center;
            font-size: 20px;
            background-color: #f8f8f8;
        }

        /* 组合区样式 */
        .combinations {
            margin-top: 20px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .story-prompt {
            margin-top: 30px;
            padding: 15px;
            background: #f8fff8;
            border: 1px solid #4CAF5020;
            border-radius: 8px;
        }

        /* 高亮样式 */
        .highlight {
            color: #d32f2f;
            font-weight: bold;
            font-size: 18px;
        }

        .combo-detail {
            margin: 5px 0;
            padding: 8px;
            border-left: 3px solid #4CAF50;
            background: #f8f8f8;
        }

        .combo-type {
            color: #666;
            font-size: 0.9em;
            margin-bottom: 3px;
        }

        /* 响应式设计 */
        @media (max-width: 768px) {
            .content-wrapper {
                flex-direction: column;
                gap: 20px;
            }
            
            .tianziqi {
                margin: 0 auto;
            }
            
            .input-group {
                width: 100%;
            }
        }

        @media (max-width: 480px) {
            .input-group {
                flex-direction: column;
            }
            
            button {
                width: 100%;
            }
        }
    </style>
</head>
<body>
<div class="main-container">
    <!-- 输入区 -->
    <div class="input-group">
        <input type="text" id="inputChars" 
               placeholder="输入4个汉字" 
               maxlength="4"
               value="心志助战">
        <button onclick="generateNew()">汉语田字棋</button>
    </div>

    <!-- 核心内容 -->
    <div class="content-wrapper">
        <!-- 田字棋区 -->
        <div class="tianziqi-section">
            <table class="tianziqi" id="grid"></table>
            <div class="story-prompt" id="storyPrompt"></div>
        </div>

        <!-- 组合区 -->
        <div class="combinations-section">
            <div id="combinations" class="combinations"></div>

        </div>
    </div>
</div>
<script>
// 修正后的组合生成函数，增加顺时针和逆时针阅读方式
function getCombinations(grid) {
    const combinations = [];

    // 横向组合（行）
    for (let i = 0; i < 4; i++) {
        combinations.push({
            text: grid[i].join(''),
            detail: `第${i+1}行（→）`
        });
        combinations.push({
            text: [...grid[i]].reverse().join(''),
            detail: `第${i+1}行（←）`
        });
    }

    // 纵向组合（列）
    for (let j = 0; j < 4; j++) {
        combinations.push({
            text: grid.map(row => row[j]).join(''),
            detail: `第${j+1}列（↓）`
        });
    }

    // 田字格组合（完整版）
    const palacePositions = [
        {x:0,y:0,name:"左上田字"}, {x:0,y:2,name:"右上田字"},
        {x:2,y:0,name:"左下田字"}, {x:2,y:2,name:"右下田字"}
    ];

    palacePositions.forEach(pos => {
        const cells = [
            grid[pos.x][pos.y],     grid[pos.x][pos.y+1],// 第一行
            grid[pos.x+1][pos.y], grid[pos.x+1][pos.y+1] // 第二行
        ].filter(c => c !== undefined);

        // 横向扫描
        combinations.push({ 
            text: cells[0]+cells[1]+cells[2]+cells[3],
            detail: `${pos.name}（行→）`
        });
        combinations.push({ 
            text: cells[1]+cells[0]+cells[3]+cells[2],
            detail: `${pos.name}（行←）`
        });

        // 纵向扫描
        combinations.push({ 
            text: cells[0]+cells[2]+cells[1]+cells[3],
            detail: `${pos.name}（列→）`
        });
        combinations.push({ 
            text: cells[1]+cells[3]+cells[0]+cells[2],
            detail: `${pos.name}（列←）`
        });
        
        // 新增：顺时针扫描（↻）- 从右上开始，按顺时针方向
        combinations.push({ 
            text: cells[1]+cells[3]+cells[2]+cells[0],
            detail: `${pos.name}（↻）`
        });
        
        // 新增：逆时针扫描（↺）- 从左上开始，按逆时针方向
        combinations.push({ 
            text: cells[0]+cells[2]+cells[3]+cells[1],
            detail: `${pos.name}（↺）`
        });
    });

    // 智能去重（保留所有生成路径）
    const unique = new Map();
    combinations.forEach(c => {
        const cleanText = c.text.replace(/undefined/g, ''); // 清除无效字符;
        if (cleanText.length === 4) {
            const entry = unique.get(cleanText) || {text: cleanText, details: []};
            entry.details.push(c.detail);
            unique.set(cleanText, entry);
        }
    });

    return Array.from(unique.values()).sort(() => Math.random() - 0.5);
}

// 修正后的生成函数
function generateNew() {
    const input = document.getElementById('inputChars').value;
    if (new Set(input).size !== 4 || input.length !== 4) {
        alert("请确保输入4个不同汉字");
        return;
    }

    // 动态生成田字棋
    const pattern = createBaseTianziqi([...input]);
    
    // 更新表格显示
    const grid = document.getElementById('grid');
    grid.innerHTML = pattern.map(row => 
        `<tr>${row.map(cell => `<td>${cell}</td>`).join('')}</tr>`
    ).join('');

    // 动态字体大小
    document.querySelectorAll('.tianziqi td').forEach(td => {
        td.style.fontSize = td.offsetWidth * 0.4 + 'px';
    });

    // 生成组合数据
    const combos = getCombinations(pattern);
    
    // 生成显示内容
    const comboList = combos.map((combo, i) => `
        <div ${i < 4 ? 'class="highlight"' : ''}>
            ${i+1}. ${combo.text}
            <div class="combo-detail">
                <div class="combo-type">生成路径：</div>
                ${combo.details.join('<br>')}
            </div>
        </div>
    `).join('');

    // 更新DOM
    document.getElementById('combinations').innerHTML = comboList;

    // 故事提示
    document.getElementById('storyPrompt').innerHTML = `
        <div style="color:#4CAF50; font-size:16px;">
            请依照以下字序，<br>创作小故事：<br>
            ${combos.slice(0,4).map(c => c.text).join('、')}
        </div>`;
}

// 初始化逻辑保持不变
window.onload = () => {
    document.getElementById('inputChars').addEventListener('keypress', e => {
        if (e.key === 'Enter') generateNew();
    });
    generateNew();
};

// 动态生成符合规则的田字棋基础结构
// 生成第三田字（左下田字）
function generateThirdPalace(chars, existingRows) {
    const palace = Array(2).fill().map(() => Array(2));
    
    // 修正：前两格从第一行和第二行的第2列取
    const sourceCols = existingRows.slice(0,2).map(row => row[1]); // 取每行第2列（索引1）
    const candidatePool = shuffle([...new Set(sourceCols)]); // 去重后随机排序
    
    // 确保候选池有2个不同字符
    if (candidatePool.length < 2) {
        throw new Error('候选池不足');
    }
    
    // 分配前两格
    [palace[0][0], palace[0][1]] = [
        candidatePool[0], 
        candidatePool[1] || candidatePool[0] // 确保有值
    ];

    // 后两个字符从剩余字符中选择
    const remainingChars = difference(chars, palace.flat());
    [palace[1][0], palace[1][1]] = shuffle(remainingChars);

    return palace;
}

function generateFourthPalaceFirstChar(tianziqi, row, col, chars) {
    // 收集约束条件
    const rowChars = tianziqi[row].slice(0, col); // 当前行已填字符
    const colChars = tianziqi.slice(0, row).map(r => r[col]); // 当前列已填字符
    
    // 合并排除列表（去重）
    const excludeChars = [...new Set([...rowChars, ...colChars])];
    
    // 计算候选字符
    let candidates = difference( chars, [...excludeChars]);
    
    // 处理不同情况
    switch(candidates.length) {
        case 2: 
            return shuffle(candidates)[0]; // 随机选择其中一个
        case 1: 
            return candidates[0]; // 唯一解
        case 0: 
            throw new Error('无有效候选字符');
        default: 
            // 当候选超过2个时，优先选择与其他田字格关联性低的字符
            const usageCount = chars.map(c => 
                tianziqi.flat().filter(x => x === c).length
            );
            return candidates.reduce((a,b) => 
                usageCount[chars.indexOf(a)] < usageCount[chars.indexOf(b)] ? a : b
            );
    }
}

function createBaseTianziqi(chars) {
    let attempt = 0;
    const MAX_ATTEMPTS = 100;

    while (attempt++ < MAX_ATTEMPTS) {
        try {
            const tianziqi = Array(4).fill().map(() => Array(4));
            
            // 第一行：完全随机
            tianziqi[0] = shuffle([...chars]);
            
            // 第二行：前两格从第一行后两格取，后两格从前两格取
            const row1Candidates = shuffle([tianziqi[0][2], tianziqi[0][3]]);
            const row1Remaining = difference(chars, row1Candidates);
            tianziqi[1] = [...row1Candidates, ...shuffle(row1Remaining)];

            // 在createBaseTianziqi函数中的使用方式
            const thirdPalace = generateThirdPalace(chars, [tianziqi[0], tianziqi[1]]);
            tianziqi[2][0] = thirdPalace[0][0];  // 第三行第一列
            tianziqi[2][1] = thirdPalace[1][0];  // 第三行第二列
            tianziqi[3][0] = thirdPalace[0][1];  // 第四行第一列
            tianziqi[3][1] = thirdPalace[1][1];  // 第四行第二列

            // 在生成第四田字时调用
            tianziqi[2][2] = generateFourthPalaceFirstChar(
                tianziqi, // 当前田字棋数组
                2,      // 行索引（第3行）
                2,      // 列索引（第3列）
                chars   // 原始字符集
            );

            // 填充剩余三个格子 
            tianziqi[3][2] = difference(chars, [tianziqi[0][2], tianziqi[1][2], tianziqi[2][2]])[0];
            tianziqi[2][3] = difference(chars, tianziqi[2].slice(0, 3))[0];
            tianziqi[3][3] = difference(chars, tianziqi[3].slice(0, 3))[0];

            // 最终校验
            if (validateTianziqi(tianziqi)) return tianziqi;
        } catch (e) {
            // 生成失败时自动重试
        }
    }
    throw new Error('无法生成有效田字棋');
}

// 实用工具函数
function shuffle(arr) {
    return [...arr].sort(() => Math.random()-0.5);
}

function difference(mainArr, excludeArr) {
    return mainArr.filter(c => !excludeArr.includes(c));
}

function selectValidChar(position, tianziqi) {
    const {row, col, exclude} = position;
    const candidates = difference(chars, [
        ...tianziqi[row].slice(0, col),  // 当前行已填
        ...exclude
    ]);
    
    if (candidates.length === 0) throw new Error('无可用字符');
    return candidates[Math.floor(Math.random() * candidates.length)];
}

function lastRemainingChar(row) {
    const remaining = difference(chars, row.filter(Boolean));
    if (remaining.length !== 1) throw new Error('最后字符异常');
    return remaining[0];
}

function validateTianziqi(tianziqi) {
    // 校验所有行
    const rowValid = tianziqi.every(row => new Set(row).size === 4);
    
    // 校验所有列
    const colValid = [...Array(4)].every((_,j) => 
        new Set(tianziqi.map(row => row[j])).size === 4
    );
    
    // 校验所有田字格
    const palaceValid = [
        [0,0],[0,2],[2,0],[2,2]
    ].every(([x,y]) => {
        const cells = [
            tianziqi[x][y],   tianziqi[x][y+1],
            tianziqi[x+1][y], tianziqi[x+1][y+1]
        ];
        return new Set(cells).size === 4;
    });

    return rowValid && colValid && palaceValid;
}
</script>
</body>
</html>